home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / file / managers / mc-3.2 / mc-3 / mc-3.2.1 / src / util.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-17  |  28.9 KB  |  1,240 lines

  1. /* Various utilities
  2.    Copyright (C) 1994, 1995, 1996 the Free Software Foundation.
  3.    Written 1994, 1995, 1996 by:
  4.    Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
  5.    Jakub Jelinek, Mauricio Plaza.
  6.  
  7.    The file_date routine is mostly from GNU's fileutils package,
  8.    written by Richard Stallman and David MacKenzie.
  9.  
  10.    This program is free software; you can redistribute it and/or modify
  11.    it under the terms of the GNU General Public License as published by
  12.    the Free Software Foundation; either version 2 of the License, or
  13.    (at your option) any later version.
  14.    
  15.    This program is distributed in the hope that it will be useful,
  16.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.    GNU General Public License for more details.
  19.  
  20.    You should have received a copy of the GNU General Public License
  21.    along with this program; if not, write to the Free Software
  22.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  23.  
  24. #include <config.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <sys/types.h>
  28. #ifdef HAVE_UNISTD_H
  29. #include <unistd.h>
  30. #endif
  31. #include <fcntl.h>
  32. #include <signal.h>        /* my_system */
  33. #include <limits.h>        /* INT_MAX */
  34. #include <sys/time.h>        /* select: timeout */
  35. #include <sys/param.h>
  36. #include <sys/types.h>
  37. #include <sys/stat.h>
  38. #include <stdarg.h>
  39. #include <errno.h>        /* my_system */
  40. #include <time.h>
  41. #include <pwd.h>
  42. #include <grp.h>
  43. #include <string.h>
  44. #include <ctype.h>
  45. #ifdef IS_AIX
  46. #  include <sys/select.h>
  47. #endif
  48.  
  49. #ifdef __linux__
  50. #  include <linux/termios.h>    /* This is needed for TIOCLINUX */
  51. #  include <sys/ioctl.h>
  52. #endif
  53. #include "fs.h"
  54. #include "fsusage.h"
  55. #include "mountlist.h"
  56.  
  57. /* From dialog.h (not wanting to include it as
  58.    it requires including a lot of other files, too) */
  59. int message (int error, char *header, char *text, ...);
  60.  
  61. #include "mad.h"
  62. #if defined(HAVE_RX_H) && defined(HAVE_REGCOMP)
  63. #include <rx.h>
  64. #else
  65. #include "regex.h"
  66. #endif
  67. #include "util.h"
  68. #include "global.h"
  69. #include "profile.h"
  70. #include "user.h"        /* expand_format */
  71. #include "../vfs/vfs.h"
  72.  
  73. /* "$Id: util.c,v 1.17 1995/02/21 19:07:33 miguel Exp $" */
  74.  
  75. char app_text [] = "Midnight-Commander";
  76.  
  77. int easy_patterns = 1;
  78. int align_extensions = 1;
  79. int tilde_trunc = 1;
  80.  
  81. #ifndef HAVE_STRDUP
  82. char *strdup (char *s)
  83. {
  84.     char *t = malloc (strlen (s)+1);
  85.     strcpy (t, s);
  86.     return t;
  87. }
  88. #endif
  89.  
  90. int is_printable (int c)
  91. {
  92.     extern int eight_bit_clean;
  93.     extern int full_eight_bits;
  94.  
  95.     c &= 0xff;
  96.     if (eight_bit_clean){
  97.     if (full_eight_bits)
  98.         return (c > 31 && c != 127);
  99.     else
  100.         return ((c >31 && c < 127) || c >= 160);
  101.     } else
  102.     return (c > 31 && c < 127);
  103. }
  104.  
  105. /* Returns the message dimensions (lines and columns) */
  106. int msglen (char *text, int *lines)
  107. {
  108.     int max = 0;
  109.     int line_len = 0;
  110.     
  111.     for (*lines = 1;*text; text++){
  112.     if (*text == '\n'){
  113.         line_len = 0;
  114.         (*lines)++;
  115.     } else {
  116.         line_len++;
  117.         if (line_len > max)
  118.         max = line_len;
  119.     }
  120.     }
  121.     return max;
  122. }
  123.  
  124. char *trim (char *s, char *d, int len)
  125. {
  126.     int source_len = strlen (s);
  127.     
  128.     if (source_len > len){
  129.     strcpy (d, s+(source_len-len));
  130.     d [0] = '.';
  131.     d [1] = '.';
  132.     d [2] = '.';
  133.     } else
  134.     strcpy (d, s);
  135.     return d;
  136. }
  137.  
  138. /* If passed an empty txt (this usually means that there is an error)
  139.  * in the upper layers, we return "/"
  140.  */
  141. char *name_trunc (char *txt, int trunc_len)
  142. {
  143.     static char x [MC_MAXPATHLEN+MC_MAXPATHLEN];
  144.     int    txt_len;
  145.     char *p;
  146.  
  147.     if (!txt)
  148.     txt = PATH_SEP_STR;
  149.     
  150.     if (trunc_len > sizeof (x)-1){
  151.     fprintf (stderr, "name_trunc: too big");
  152.     trunc_len = sizeof (x)-1;
  153.     }
  154.     txt_len = strlen (txt);
  155.     if (txt_len <= trunc_len)
  156.     strcpy (x, txt);
  157.     else if (tilde_trunc){
  158.     int y = trunc_len % 2;
  159.     strncpy (x, txt, (trunc_len/2)+y);
  160.     strncpy (x+(trunc_len/2)+y, txt+txt_len-(trunc_len/2), trunc_len/2);
  161.     x [(trunc_len/2)+y] = '~';
  162.     } else {
  163.     strncpy (x, txt, trunc_len-1);
  164.     x [trunc_len-1] = '>';
  165.     }
  166.     x [trunc_len] = 0;
  167.     for (p = x; *p; p++)
  168.         if (!is_printable (*p))
  169.             *p = '?';
  170.     return x;
  171. }
  172.  
  173. char *size_trunc (long int size)
  174. {
  175.     static char x [30];
  176.     long int divisor = 1;
  177.     char *xtra = "";
  178.     
  179.     if (size > 999999999L){
  180.     divisor = 1024;
  181.     xtra = "kb";
  182.     if (size/divisor > 999999999L){
  183.         divisor = 1024*1024;
  184.         xtra = "Mb";
  185.     }
  186.     }
  187.     sprintf (x, "%ld%s", (size/divisor), xtra);
  188.     return x;
  189. }
  190.  
  191. char *size_trunc_sep (long int size)
  192. {
  193.     static char x [60];
  194.     int  count;
  195.     char *p, *d, *y;
  196.  
  197.     p = y = size_trunc (size);
  198.     p += strlen (p) - 1;
  199.     d = x + sizeof (x) - 1;
  200.     *d-- = 0;
  201.     while (p >= y && isalpha (*p))
  202.     *d-- = *p--;
  203.     for (count = 0; p >= y; count++){
  204.     if (count == 3){
  205.         *d-- = ',';
  206.         count = 0;
  207.     }
  208.     *d-- = *p--;
  209.     }
  210.     d++;
  211.     if (*d == ',')
  212.     d++;
  213.     return d;
  214. }
  215.  
  216. int is_exe (mode_t mode)
  217. {
  218.     if ((S_IXUSR & mode) || (S_IXGRP & mode) || (S_IXOTH & mode))
  219.     return 1;
  220.     return 0;
  221. }
  222.  
  223. #define ismode(n,m) ((n & m) == m)
  224.  
  225. char *string_perm (mode_t mode_bits)
  226. {
  227.     static char mode [11];
  228.  
  229.     strcpy (mode, "----------");
  230.     if (ismode (mode_bits, S_IFDIR)) mode [0] = 'd';
  231. #ifdef S_IFSOCK
  232.     if (ismode (mode_bits, S_IFSOCK)) mode [0] = 's';
  233. #endif
  234.     if (ismode (mode_bits, S_IXOTH)) mode [9] = 'x';
  235.     if (ismode (mode_bits, S_IWOTH)) mode [8] = 'w';
  236.     if (ismode (mode_bits, S_IROTH)) mode [7] = 'r';
  237.     if (ismode (mode_bits, S_IXGRP)) mode [6] = 'x';
  238.     if (ismode (mode_bits, S_IWGRP)) mode [5] = 'w';
  239.     if (ismode (mode_bits, S_IRGRP)) mode [4] = 'r';
  240.     if (ismode (mode_bits, S_IXUSR)) mode [3] = 'x';
  241.     if (ismode (mode_bits, S_IWUSR)) mode [2] = 'w';
  242.     if (ismode (mode_bits, S_IRUSR)) mode [1] = 'r';
  243.     if (ismode (mode_bits, S_ISUID)) mode [3] = (mode [3] == 'x') ? 's' : 'S';
  244.     if (ismode (mode_bits, S_ISGID)) mode [6] = (mode [6] == 'x') ? 's' : 'S';
  245.     if (ismode (mode_bits, S_IFCHR)) mode [0] = 'c';
  246.     if (ismode (mode_bits, S_IFBLK)) mode [0] = 'b';
  247.     if (ismode (mode_bits, S_ISVTX)) mode [9] = (mode [9] == 'x') ? 't' : 'T';
  248.     if (ismode (mode_bits, S_IFLNK)) mode [0] = 'l';
  249.     if (ismode (mode_bits, S_IFIFO)) mode [0] = 's';
  250.     return mode;
  251. }
  252.  
  253. char *strip_home(char *dir)
  254. {
  255.     static char newdir [MC_MAXPATHLEN], *p, *q;
  256.  
  257.     if (home_dir && !strncmp (dir, home_dir, strlen (home_dir))){
  258.     newdir [0] = '~';
  259.     strcpy (&newdir [1], &dir [strlen (home_dir)]);
  260.     return newdir;
  261.     } 
  262. #ifdef USE_NETCODE    
  263.     else if (!strncmp (dir, "ftp://", 6)) {
  264.         if ((p = strchr (dir + 6, PATH_SEP)) != NULL) {
  265.             *p = 0;
  266.             if ((q = ftpfs_gethome (dir)) == NULL)
  267.         return dir;
  268.             *p = PATH_SEP;
  269.             if (q != NULL && strcmp (q, PATH_SEP_STR) && !strncmp (p, q, strlen (q) - 1)) {
  270.                 strncpy (newdir, dir, p - dir);
  271.                 strcpy (newdir + (p - dir), "/~");
  272.                 strcat (newdir, p + strlen (q) - 1);
  273.                 return newdir;
  274.             }
  275.         }
  276.     } else if (!strncmp (dir, "mc:", 3)) {
  277.         if ((p = strchr (dir + 3, PATH_SEP)) != NULL) {
  278.             *p = 0;
  279.             if ((q = mcfs_gethome (dir)) == NULL)
  280.         return dir;
  281.             *p = PATH_SEP;
  282.             if (q != NULL && strcmp (q, PATH_SEP_STR) && !strncmp (p, q, strlen (q) - 1)) {
  283.                 strncpy (newdir, dir, p - dir);
  284.                 strcpy (newdir + (p - dir), "/~");
  285.                 strcat (newdir, p + strlen (q) - 1);
  286.                 free (q);
  287.                 return newdir;
  288.             } else if (q != NULL) {
  289.                 free (q);
  290.             }
  291.         }
  292.     }
  293. #endif    
  294.     return dir;
  295. }
  296.  
  297. static char *maybe_start_group (char *d, int do_group, int *was_wildcard)
  298. {
  299.     if (!do_group)
  300.     return d;
  301.     if (*was_wildcard)
  302.     return d;
  303.     *was_wildcard = 1;
  304.     *d++ = '\\';
  305.     *d++ = '(';
  306.     return d;
  307. }
  308.  
  309. static char *maybe_end_group (char *d, int do_group, int *was_wildcard)
  310. {
  311.     if (!do_group)
  312.     return d;
  313.     if (!*was_wildcard)
  314.     return d;
  315.     *was_wildcard = 0;
  316.     *d++ = '\\';
  317.     *d++ = ')';
  318.     return d;
  319. }
  320.  
  321. /* If shell patterns are on converts a shell pattern to a regular
  322.    expression. Called by regexp_match and mask_rename. */
  323. /* Shouldn't we support [a-fw] type wildcards as well ?? */
  324. char *convert_pattern (char *pattern, int match_type, int do_group)
  325. {
  326.     char *s, *d;
  327.     static char new_pattern [100];
  328.     int was_wildcard = 0;
  329.  
  330.     if (easy_patterns){
  331.     d = new_pattern;
  332.     if (match_type == match_file)
  333.         *d++ = '^';
  334.     for (s = pattern; *s; s++, d++){
  335.         switch (*s){
  336.         case '*':
  337.         d = maybe_start_group (d, do_group, &was_wildcard);
  338.         *d++ = '.';
  339.         *d   = '*';
  340.         break;
  341.         
  342.         case '?':
  343.         d = maybe_start_group (d, do_group, &was_wildcard);
  344.         *d = '.';
  345.         break;
  346.         
  347.         case '.':
  348.         d = maybe_end_group (d, do_group, &was_wildcard);
  349.         *d++ = '\\';
  350.         *d   = '.';
  351.         break;
  352.  
  353.         default:
  354.         d = maybe_end_group (d, do_group, &was_wildcard);
  355.         *d = *s;
  356.         break;
  357.         }
  358.     }
  359.     d = maybe_end_group (d, do_group, &was_wildcard);
  360.     if (match_type == match_file)
  361.         *d++ = '$';
  362.     *d = 0;
  363.     return new_pattern;
  364.     } else
  365.     return pattern;
  366. }
  367.  
  368. /* 1 if string matches
  369.    0 if string doesn't match
  370.    -1 if error in pattern */
  371.  
  372. int regexp_match (char *pattern, char *string, int match_type)
  373. {
  374.     static regex_t r;
  375.     static char *old_pattern = NULL;
  376.     static int old_type;
  377.     int    rval;
  378.  
  379.     if (!old_pattern || strcmp (old_pattern, pattern) || old_type != match_type){
  380.     if (old_pattern){
  381.         regfree (&r);
  382.         free (old_pattern);
  383.     }
  384.     pattern = convert_pattern (pattern, match_type, 0);
  385.     if (regcomp (&r, pattern, REG_EXTENDED|REG_NOSUB))
  386.         return -1;
  387.     old_pattern = strdup (pattern);
  388.     old_type = match_type;
  389.     }
  390.     rval = !regexec (&r, string, 0, NULL, 0);
  391.     return rval;
  392. }
  393.  
  394. char *extension (char *filename)
  395. {
  396.     char *d;
  397.  
  398.     if (!strlen (filename))
  399.     return "";
  400.     
  401.     d = filename + strlen (filename) - 1;
  402.     for (;d >= filename; d--){
  403.     if (*d == '.')
  404.         return d+1;
  405.     }
  406.     return "";
  407. }
  408.  
  409. /* This routine uses the fact that x is at most 14 chars or so */
  410. char *split_extension (char *x, int pad)
  411. {
  412.     return x;
  413.  
  414.     /* Buggy code 
  415.     if (!align_extensions)
  416.     return x;
  417.  
  418.     if (strlen (x) >= pad)
  419.     return x;
  420.     
  421.     if ((ext = extension (x)) == x || *ext == 0)
  422.     return x;
  423.  
  424.     strcpy (xbuf, x);
  425.     for (i = strlen (x); i < pad; i++)
  426.     xbuf [i] = ' ';
  427.     xbuf [pad] = 0;
  428.  
  429.     l = strlen (ext);
  430.     for (i = 0; i < l; i++)
  431.     xbuf [pad-i] = *(ext+l-i-1);
  432.     for (i = xbuf + (ext - x); i < 
  433.     return xbuf; */
  434. }
  435.  
  436. #ifndef HAVE_MAD
  437. void *do_xmalloc (int size)
  438. {
  439.     void *m = malloc (size);
  440.  
  441.     if (!m){
  442.     fprintf (stderr, "memory exhausted\n");
  443.     exit (1);
  444.     }
  445.     return m;
  446. }
  447. #endif /* HAVE_MAD */
  448.  
  449. int get_int (char *file, char *key, int def)
  450. {
  451.     return GetPrivateProfileInt (app_text, key, def, file);
  452. }
  453.  
  454. int set_int (char *file, char *key, int value)
  455. {
  456.     char buffer [6];
  457.  
  458.     sprintf (buffer,  "%d", value);
  459.     return WritePrivateProfileString (app_text, key, buffer, file);
  460. }
  461.  
  462. int exist_file (char *name)
  463. {
  464.     return access (name, R_OK) == 0;
  465. }
  466.  
  467. char *load_file (char *filename)
  468. {
  469.     FILE *data_file;
  470.     struct stat s;
  471.     char *data;
  472.     long read_size;
  473.     
  474.     if (stat (filename, &s) != 0){
  475.     return 0;
  476.     }
  477.     if ((data_file = fopen (filename, "r")) == NULL){
  478.     return 0;
  479.     }
  480.     data = (char *) xmalloc (s.st_size+1, "util, load_file");
  481.     read_size = fread (data, 1, s.st_size, data_file);
  482.     data [read_size] = 0;
  483.     fclose (data_file);
  484.  
  485.     if (read_size > 0)
  486.     return data;
  487.     else
  488.     return 0;
  489. }
  490.  
  491. char *file_date (time_t when)
  492. {
  493.     static char timebuf [40];
  494.     time_t current_time = time ((time_t) 0);
  495.  
  496. #ifdef _OS_NT
  497.     char   *p;
  498.  
  499.     p = ctime (&when);
  500.     p ? strcpy (timebuf, p) : p = "-----";
  501. #else
  502.     strcpy (timebuf, ctime (&when));
  503. #endif
  504.     if (current_time > when + 6L * 30L * 24L * 60L * 60L /* Old. */
  505.     || current_time < when - 60L * 60L) /* In the future. */
  506.     {
  507.     /* The file is fairly old or in the future.
  508.        POSIX says the cutoff is 6 months old;
  509.        approximate this by 6*30 days.
  510.        Allow a 1 hour slop factor for what is considered "the future",
  511.        to allow for NFS server/client clock disagreement.
  512.        Show the year instead of the time of day.  */
  513.     strcpy (timebuf + 11, timebuf + 19);
  514.     }
  515.     timebuf[16] = 0;
  516.     return &timebuf [4];
  517. }
  518.  
  519. /* Like file_date, but packs the data to fit in 10 columns */
  520. char *file_date_pck (time_t when)
  521. {
  522.     /* FIXME: Should return only 10 chars, not 14 */
  523.     return file_date (when);
  524. }
  525.  
  526. char *extract_line (char *s, char *top)
  527. {
  528.     static char tmp_line [500];
  529.     char *t = tmp_line;
  530.     
  531.     while (*s && *s != '\n' && (t - tmp_line) < sizeof (tmp_line)-1 && s < top)
  532.     *t++ = *s++;
  533.     *t = 0;
  534.     return tmp_line;
  535. }
  536.  
  537. /* FIXME: I should write a faster version of this (Aho-Corasick stuff) */
  538. char *icase_search (char *text, char *data)
  539. {
  540.     char *d = text;
  541.     char *e = data;
  542.     register char c, c1;
  543.     
  544.     for (;*e; e++){
  545.         c = *d | 0x20;
  546.         c1 = *e | 0x20;
  547.     if (c == c1 && ((c >= 'a' && c <= 'z' && c1 >= 'a' && c1 <= 'z') ||
  548.         *d == *e))
  549.         d++;
  550.     else {
  551.         e -= d - text;
  552.         d = text;
  553.     }
  554.     if (!*d)
  555.         return e+1;
  556.     }
  557.     return 0;
  558. }
  559.  
  560. /* The basename routine */
  561. char *x_basename (char *s)
  562. {
  563.     char  *where;
  564.     return ((where = strrchr (s, PATH_SEP)))? where + 1 : s;
  565. }
  566.  
  567. char *get_full_name (char *dir, char *file)
  568. {
  569.     int i;
  570.     char *d = malloc (strlen (dir) + strlen (file) + 2);
  571.  
  572.     strcpy (d, dir);
  573.     i = strlen (dir);
  574.     if (dir [i - 1] != PATH_SEP || dir [i] != 0)
  575.     strcat (d, PATH_SEP_STR);
  576.     file = x_basename (file);
  577.     strcat (d, file);
  578.     return d;
  579. }
  580.  
  581. void my_putenv (char *name, char *data)
  582. {
  583.     char *full;
  584.  
  585.     full = xmalloc (strlen (name) + strlen (data) + 2, "util, my_putenv");
  586.     sprintf (full, "%s=%s", name, data);
  587.     putenv (full);
  588.     /* WARNING: NEVER FREE THE full VARIABLE!!!!!!!!!!!!!!!!!!!!!!!! */
  589.     /* It is used by putenv. Freeing it will corrupt the environment */
  590. }
  591.  
  592. #if 0
  593. static void my_putenv_expand (char *name, char macro_code)
  594. {
  595.     char *data;
  596.  
  597.     data = expand_format (macro_code);
  598.     my_putenv (name, data);
  599.     free (data);
  600. }
  601.  
  602. /* Puts some status information in to the environment so that
  603.    processes to be executed can access it. */
  604. static void prepare_environment (void)
  605. {
  606.     my_putenv_expand ("MC_CURRENT_DIR", 'd');
  607.     my_putenv_expand ("MC_OTHER_DIR", 'D');
  608.     my_putenv_expand ("MC_CURRENT_FILE", 'f');
  609.     my_putenv_expand ("MC_OTHER_FILE", 'F');
  610.     my_putenv_expand ("MC_CURRENT_TAGGED", 't');
  611.     my_putenv_expand ("MC_OTHER_TAGGED", 'T');
  612.     /* MC_CONTROL_FILE has been added to environment on startup */
  613. }
  614. #endif
  615.  
  616. /* Since ncurses uses a handler that automatically refreshes the */
  617. /* screen after a SIGCONT, and we don't want this behavior when */
  618. /* spawning a child, we save the original handler here */
  619. void save_stop_handler (void)
  620. {
  621.     sigaction (SIGTSTP, NULL, &startup_handler);
  622. }
  623.  
  624. char *unix_error_string (int error_num)
  625. {
  626.     static char buffer [256];
  627.     char *error_msg;
  628.     
  629. #ifdef HAVE_STRERROR
  630.     error_msg = strerror (error_num);
  631. #else
  632.     extern int sys_nerr;
  633.     extern char *sys_errlist [];
  634.     if ((0 <= error_num) && (error_num < sys_nerr))
  635.     error_msg = sys_errlist[error_num];
  636.     else
  637.     error_msg = "strange errno";
  638. #endif
  639.     sprintf (buffer, "%s (%d)", error_msg, error_num);
  640.     return buffer;
  641. }
  642.  
  643. char *copy_strings (char *first,...)
  644. {
  645.     va_list ap;
  646.     int len;
  647.     char *data, *result;
  648.  
  649.     if (!first)
  650.     return 0;
  651.     
  652.     len = strlen (first);
  653.     va_start (ap, first);
  654.  
  655.     while ((data = va_arg (ap, char *))!=0)
  656.     len += strlen (data);
  657.  
  658.     len++;
  659.  
  660.     result = xmalloc (len, "copy_strings");
  661.     va_end (ap);
  662.     va_start (ap, first);
  663.     strcpy (result, first);
  664.     while ((data = va_arg (ap, char *)) != 0)
  665.     strcat (result, data);
  666.     va_end (ap);
  667.  
  668.     return result;
  669. }
  670.     
  671. long blocks2kilos (int blocks, int bsize)
  672. {
  673.     if (bsize > 1024){
  674.     return blocks * (bsize / 1024);
  675.     } else if (bsize < 1024){
  676.     return blocks / (1024 /bsize);
  677.     } else
  678.     return blocks;
  679. }
  680.  
  681. static struct mount_entry *mount_list;
  682.  
  683. void init_my_statfs (void)
  684. {
  685. #ifndef NO_INFOMOUNT
  686.     mount_list = read_filesystem_list (1, 1);
  687. #endif
  688. }
  689.  
  690. void my_statfs (struct my_statfs *myfs_stats, char *path)
  691. {
  692.     int i, len = 0;
  693.  
  694. #ifndef NO_INFOMOUNT
  695.     struct mount_entry *entry = NULL;
  696.     struct mount_entry *temp = mount_list;
  697.     struct fs_usage fs_use;
  698.  
  699.     while (temp){
  700.     i = strlen (temp->me_mountdir);
  701.     if (i > len && (strncmp (path, temp->me_mountdir, i) == 0))
  702.         if (!entry || (path [i] == PATH_SEP || path [i] == 0)){
  703.         len = i;
  704.         entry = temp;
  705.         }
  706.     temp = temp->me_next;
  707.     }
  708.  
  709.     if (entry){
  710.     get_fs_usage (entry->me_mountdir, &fs_use);
  711.  
  712.     myfs_stats->type = entry->me_dev;
  713.     myfs_stats->typename = entry->me_type;
  714.     myfs_stats->mpoint = entry->me_mountdir;
  715.     myfs_stats->device = entry->me_devname;
  716.     myfs_stats->avail = getuid () ? fs_use.fsu_bavail/2 : fs_use.fsu_bfree/2;
  717.     myfs_stats->total = fs_use.fsu_blocks/2;
  718.     myfs_stats->nfree = fs_use.fsu_ffree;
  719.     myfs_stats->nodes = fs_use.fsu_files;
  720.     } else
  721. #endif
  722. #ifdef _OS_NT
  723.     DWORD lpSectorsPerCluster, lpBytesPerSector, lpFreeClusters, lpClusters;
  724.        DWORD           lpMaximumComponentLength, dw, lpFileSystemFlags;
  725.        static char     lpVolumeNameBuffer[256], lpFileSystemNameBuffer[30];
  726.  
  727.        GetDiskFreeSpace(NULL, &lpSectorsPerCluster, &lpBytesPerSector,
  728.             &lpFreeClusters, &lpClusters);
  729.  
  730.        /* KBytes available */
  731.        myfs_stats->avail = lpSectorsPerCluster * lpBytesPerSector * lpFreeClusters / 1024;
  732.        
  733.        /* KBytes total */
  734.        myfs_stats->total = lpSectorsPerCluster * lpBytesPerSector * lpClusters / 1024; 
  735.        myfs_stats->nfree = lpFreeClusters;
  736.        myfs_stats->nodes = lpClusters;
  737.  
  738.        GetVolumeInformation(NULL, lpVolumeNameBuffer, 255, NULL,
  739.                 &lpMaximumComponentLength, &lpFileSystemFlags,
  740.                 lpFileSystemNameBuffer, 30);
  741.  
  742.        myfs_stats->mpoint = lpFileSystemNameBuffer;
  743.        myfs_stats->device = lpVolumeNameBuffer;
  744.  
  745.  
  746.        myfs_stats->type = GetDriveType(NULL);
  747.        switch (myfs_stats->type) {
  748.        /*
  749.         * mmm. DeviceIoControl may fail if you are not root case
  750.         * F5_1Pt2_512,            5.25", 1.2MB,  512 bytes/sector
  751.         * myfs_stats->typename = "5.25\" 1.2MB"; break; case
  752.         * F3_1Pt44_512,           3.5",  1.44MB, 512 bytes/sector
  753.         * myfs_stats->typename = "3.5\" 1.44MB"; break; case
  754.         * F3_2Pt88_512,           3.5",  2.88MB, 512 bytes/sector
  755.         * myfs_stats->typename = "3.5\" 2.88MB"; break; case
  756.         * F3_20Pt8_512,           3.5",  20.8MB, 512 bytes/sector
  757.         * myfs_stats->typename = "3.5\" 20.8MB"; break; case
  758.         * F3_720_512,             3.5",  720KB,  512 bytes/sector
  759.         * myfs_stats->typename = "3.5\" 720MB"; break; case
  760.         * F5_360_512,             5.25", 360KB,  512 bytes/sector
  761.         * myfs_stats->typename = "5.25\" 360KB"; break; case
  762.         * F5_320_512,             5.25", 320KB,  512 bytes/sector
  763.         * case F5_320_1024,       5.25", 320KB,  1024
  764.         * bytes/sector myfs_stats->typename = "5.25\" 320KB"; break;
  765.         * case F5_180_512,        5.25", 180KB,  512
  766.         * bytes/sector myfs_stats->typename = "5.25\" 180KB"; break;
  767.         * case F5_160_512,        5.25", 160KB,  512
  768.         * bytes/sector myfs_stats->typename = "5.25\" 160KB"; break;
  769.         * case RemovableMedia,    Removable media other than
  770.         * floppy myfs_stats->typename = "Removable"; break; case
  771.         * FixedMedia              Fixed hard disk media
  772.         * myfs_stats->typename = "Hard Disk"; break; case Unknown:
  773.         * Format is unknown
  774.         */
  775.        case DRIVE_REMOVABLE:
  776.                myfs_stats->typename = "Removable";
  777.                break;
  778.        case DRIVE_FIXED:
  779.                myfs_stats->typename = "Hard Disk";
  780.                break;
  781.        case DRIVE_REMOTE:
  782.                myfs_stats->typename = "Networked";
  783.                break;
  784.        case DRIVE_CDROM:
  785.                myfs_stats->typename = "CD-ROM";
  786.                break;
  787.        case DRIVE_RAMDISK:
  788.                myfs_stats->typename = "RAM disk";
  789.                break;
  790.        default:
  791.                myfs_stats->typename = "unknown";
  792.                break;
  793.        };
  794.  
  795.        if (0)
  796. #endif
  797.     {
  798.     myfs_stats->type = 0;
  799.     myfs_stats->mpoint = "unknown";
  800.     myfs_stats->device = "unknown";
  801.     myfs_stats->avail = 0;
  802.     myfs_stats->total = 0;
  803.     myfs_stats->nfree = 0;
  804.     myfs_stats->nodes = 0;
  805.     }
  806. }
  807.  
  808. char *skip_separators (char *s)
  809. {
  810.     for (;*s; s++)
  811.     if (*s != ' ' && *s != '\t' && *s != ',')
  812.         break;
  813.     return s;
  814. }
  815.  
  816. char *skip_numbers (char *s)
  817. {
  818.     for (;*s; s++)
  819.     if (!isdigit (*s))
  820.         break;
  821.     return s;
  822. }
  823.  
  824. /* Remove all control sequences from the argument string.  We define
  825.  * "control sequence", in a sort of pidgin BNF, as follows:
  826.  *
  827.  * control-seq = Esc non-'['
  828.  *           | Esc '[' (0 or more digits or ';' or '?') (any other char)
  829.  *
  830.  * This scheme works for all the terminals described in my termcap /
  831.  * terminfo databases, except the Hewlett-Packard 70092 and some Wyse
  832.  * terminals.  If I hear from a single person who uses such a terminal
  833.  * with MC, I'll be glad to add support for it.  (Dugan)
  834.  */
  835.  
  836. char *strip_ctrl_codes (char *s)
  837. {
  838.     int i;  /* Current length of the string's correct (stripped) prefix */
  839.     int j;  /* Number of control characters we have skipped so far */
  840.  
  841.     if (!s)
  842.     return 0;
  843.     
  844.     for (i = 0, j = 0; s [i+j]; ++i)
  845.     if (s [i+j] != ESC_CHAR){
  846.         if (j)
  847.         s [i] = s [i+j];
  848.     } else {
  849.         ++j;
  850.         if (s [i+j++] == '[')
  851.         while (strchr ("0123456789;?", s [i+j++]))
  852.             /* Skip the control sequence's arguments */ ;
  853.         --i;
  854.     }
  855.     s[i] = 0;
  856.     return s;
  857. }
  858.  
  859. #ifndef HAVE_STRCASECMP
  860. /* At least one version of HP/UX lacks this */
  861. /* Assumes ASCII encoding */
  862. int strcasecmp (char *s, char *d)
  863. {
  864.     register char result;
  865.  
  866.     while (1){
  867.     if (result = (0x20 | *s) - (0x20 | *d))
  868.         break;
  869.     if (!*s)
  870.         return 0;
  871.     s++;
  872.     d++;
  873.     }
  874.     return result;
  875. }
  876. #endif /* HAVE_STRCASECMP */
  877.  
  878. #ifndef HAVE_VFS
  879. /* getwd is better than getcwd, the later uses a popen ("pwd"); */
  880. char *get_current_wd (char *buffer, int size)
  881. {
  882.     char *p;
  883.  
  884. #ifdef HAVE_GETWD
  885.     p = (char *) getwd (buffer);
  886. #else
  887.     p = getcwd (buffer, size);
  888. #endif
  889.     return p;
  890. }
  891. #endif
  892.  
  893. #define CHECK(x) if (x == -1) return 0;
  894.  
  895. long get_small_endian_long (int fd)
  896. {
  897.     unsigned char a, b, c, d;
  898.  
  899.     /* It needs to be read one byte at the time to avoid endianess
  900.        portability problems */
  901.     CHECK (mc_read (fd, &a, 1));
  902.     CHECK (mc_read (fd, &b, 1));
  903.     CHECK (mc_read (fd, &c, 1));
  904.     CHECK (mc_read (fd, &d, 1));
  905.     return (d << 24) | (c << 16) | (b << 8) | a;
  906. }
  907.  
  908. /* This function returns 0 if the file is not in gunzip format  */
  909. /* or how much memory must be allocated to load the gziped file */
  910. /* Warning: this function moves the current file pointer */
  911. long int is_gunzipable (int fd)
  912. {
  913.      unsigned char magic [4];
  914.  
  915.     /* Read the magic signature */
  916.     CHECK (mc_read (fd, &magic [0], 1));
  917.     CHECK (mc_read (fd, &magic [1], 1));
  918.     CHECK (mc_read (fd, &magic [2], 1));
  919.     CHECK (mc_read (fd, &magic [3], 1));
  920.     
  921.     /* GZIP_MAGIC and OLD_GZIP_MAGIC */
  922.     if (magic [0] == 037 && (magic [1] == 0213 || magic [1] == 0236)){
  923.     /* Read the uncompressed size of the file */
  924.     mc_lseek (fd, -4, SEEK_END);
  925.     return get_small_endian_long (fd);
  926.     }
  927.  
  928.      /* PKZIP_MAGIC */
  929.      if (magic [0] == 0120 && magic [1] == 0113 && magic [2] == 003 &&
  930.      magic [3] == 004){
  931.      /* Read compression type */
  932.      mc_lseek (fd, 8, SEEK_SET);
  933.      CHECK (mc_read (fd, &magic [0], 1));
  934.      CHECK (mc_read (fd, &magic [1], 1));
  935.      /* Gzip can handle only deflated (8) or stored (0) files */
  936.      if ((magic [0] != 8 && magic [0] != 0) || magic [1] != 0)
  937.          return 0;
  938.      /* Read the uncompressed size of the first file in the archive */
  939.      mc_lseek (fd, 22, SEEK_SET);
  940.      return get_small_endian_long (fd);
  941.      }
  942.  
  943.      /* PACK_MAGIC and LZH_MAGIC and compress magic */
  944.      if (magic [0] == 037 &&
  945.      (magic [1] ==  036 || magic [1] == 0240 || magic [1] == 0235)){
  946.      
  947.      /* In case the file is packed, sco lzhed or compress_magic, the */
  948.      /* program guesses that the uncompressed size is (at most) four */
  949.      /* times the length of the compressed size, if the compression  */
  950.      /* ratio is more than 4:1 the end of the file is not displayed  */
  951.      return 4*mc_lseek (fd, 0, SEEK_END);
  952.     }
  953.     return 0;
  954. }
  955.  
  956. /* Hooks */
  957. void add_hook (Hook **hook_list, void (*hook_fn)(void *), void *data)
  958. {
  959.     Hook *new_hook = xmalloc (sizeof (Hook), "add_hook");
  960.  
  961.     new_hook->hook_fn = hook_fn;
  962.     new_hook->next    = *hook_list;
  963.     new_hook->hook_data = data;
  964.       
  965.     *hook_list = new_hook;
  966. }
  967.  
  968. void execute_hooks (Hook *hook_list)
  969. {
  970.     Hook *new_hook = 0;
  971.     Hook *p;
  972.  
  973.     /* We copy the hook list first so tahat we let the hook
  974.      * function call delete_hook
  975.      */
  976.     
  977.     while (hook_list){
  978.     add_hook (&new_hook, hook_list->hook_fn, hook_list->hook_data);
  979.     hook_list = hook_list->next;
  980.     }
  981.     p = new_hook;
  982.     
  983.     while (new_hook){
  984.     (*new_hook->hook_fn)(new_hook->hook_data);
  985.     new_hook = new_hook->next;
  986.     }
  987.     
  988.     for (hook_list = p; hook_list;){
  989.     p = hook_list;
  990.     hook_list = hook_list->next;
  991.     free (p);
  992.     }
  993. }
  994.  
  995. void delete_hook (Hook **hook_list, void (*hook_fn)(void *))
  996. {
  997.     Hook *current, *new_list, *next;
  998.  
  999.     new_list = 0;
  1000.     
  1001.     for (current = *hook_list; current; current = next){
  1002.     next = current->next;
  1003.     if (current->hook_fn == hook_fn)
  1004.         free (current);
  1005.     else
  1006.         add_hook (&new_list, current->hook_fn, current->hook_data);
  1007.     }
  1008.     *hook_list = new_list;
  1009. }
  1010.  
  1011. int hook_present (Hook *hook_list, void (*hook_fn)(void *))
  1012. {
  1013.     Hook *p;
  1014.     
  1015.     for (p = hook_list; p; p = p->next)
  1016.     if (p->hook_fn == hook_fn)
  1017.         return 1;
  1018.     return 0;
  1019. }
  1020.  
  1021. void wipe_password (char *passwd)
  1022. {
  1023.     char *p = passwd;
  1024.     
  1025.     for (;*p ; p++)
  1026.         *p = 0;
  1027.     free (passwd);
  1028. }
  1029.  
  1030. /* Convert "\E" -> esc character and ^x to control-x key and ^^ to ^ key */
  1031. /* Returns a newly allocated string */
  1032. char *convert_controls (char *s)
  1033. {
  1034.     char *valcopy = strdup (s);
  1035.     char *p, *q;
  1036.  
  1037.     /* Parse the escape special character */
  1038.     for (p = s, q = valcopy; *p;){
  1039.     if (*p == '\\'){
  1040.         p++;
  1041.         if ((*p == 'e') || (*p == 'E')){
  1042.         p++;
  1043.         *q++ = ESC_CHAR;
  1044.         }
  1045.     } else {
  1046.         if (*p == '^'){
  1047.         p++;
  1048.         if (*p == '^')
  1049.             *q++ = *p++;
  1050.         else {
  1051.             *p = (*p | 0x20);
  1052.             if (*p >= 'a' && *p <= 'z') {
  1053.                 *q++ = *p++ - 'a' + 1;
  1054.             } else
  1055.                 p++;
  1056.         }
  1057.         } else
  1058.         *q++ = *p++;
  1059.     }
  1060.     }
  1061.     *q++ = 0;
  1062.     return valcopy;
  1063. }
  1064.  
  1065. /* Reverse the string */
  1066. char *reverse_string (char *string)
  1067. {
  1068.     int len = strlen (string);
  1069.     int i;
  1070.     const int steps = len/2;
  1071.     
  1072.     for (i = 0; i < steps; i++){
  1073.     char c = string [i];
  1074.     
  1075.     string [i] = string [len-i-1];
  1076.     string [len-i-1] = c;
  1077.     }
  1078.     return string;
  1079. }
  1080.  
  1081. char *resolve_symlinks (char *path)
  1082. {
  1083.     char *buf, *buf2, *p, *q, *r, c;
  1084.     int len;
  1085.     struct stat mybuf;
  1086.     
  1087.     if (*path != PATH_SEP)
  1088.         return NULL;
  1089.     r = buf = xmalloc (MC_MAXPATHLEN, "resolve symlinks");
  1090.     buf2 = xmalloc (MC_MAXPATHLEN, "resolve symlinks"); 
  1091.     *r++ = PATH_SEP;
  1092.     *r = 0;
  1093.     p = path;
  1094.     for (;;) {
  1095.     q = strchr (p + 1, PATH_SEP);
  1096.     if (!q) {
  1097.         q = strchr (p + 1, 0);
  1098.         if (q == p + 1)
  1099.             break;
  1100.     }
  1101.     c = *q;
  1102.     *q = 0;
  1103.     if (mc_lstat (path, &mybuf) < 0) {
  1104.         free (buf);
  1105.         free (buf2);
  1106.         *q = c;
  1107.         return NULL;
  1108.     }
  1109.     if (!S_ISLNK (mybuf.st_mode))
  1110.         strcpy (r, p + 1);
  1111.     else {
  1112.         len = mc_readlink (path, buf2, MC_MAXPATHLEN);
  1113.         if (len < 0) {
  1114.         free (buf);
  1115.         free (buf2);
  1116.         *q = c;
  1117.         return NULL;
  1118.         }
  1119.         buf2 [len] = 0;
  1120.         if (*buf2 == PATH_SEP)
  1121.         strcpy (buf, buf2);
  1122.         else
  1123.         strcpy (r, buf2);
  1124.     }
  1125.     canonicalize_pathname (buf);
  1126.     r = strchr (buf, 0);
  1127.     if (!*r || *(r - 1) != PATH_SEP) {
  1128.         *r++ = PATH_SEP;
  1129.         *r = 0;
  1130.     }
  1131.     *q = c;
  1132.     p = q;
  1133.     if (!c)
  1134.         break;
  1135.     }
  1136.     if (!*buf)
  1137.     strcpy (buf, PATH_SEP_STR);
  1138.     else if (*(r - 1) == PATH_SEP && r != buf + 1)
  1139.     *(r - 1) = 0;
  1140.     free (buf2);
  1141.     return buf;
  1142. }
  1143.  
  1144. /* Finds out a relative path from first to second, i.e. goes as many ..
  1145.  * as needed up in first and then goes down using second */
  1146. char *diff_two_paths (char *first, char *second) 
  1147. {
  1148.     char *p, *q, *r, *s, *buf = 0;
  1149.     int i, j, prevlen = -1, currlen;
  1150.     
  1151.     first = resolve_symlinks (first);
  1152.     if (first == NULL)
  1153.         return NULL;
  1154.     for (j = 0; j < 2; j++) {
  1155.     p = first;
  1156.     if (j) {
  1157.         second = resolve_symlinks (second);
  1158.         if (second == NULL) {
  1159.         free (first);
  1160.             return buf;
  1161.         }
  1162.     }
  1163.     q = second;
  1164.     for (;;) {
  1165.         r = strchr (p, PATH_SEP);
  1166.         s = strchr (q, PATH_SEP);
  1167.         if (!r || !s)
  1168.           break;
  1169.         *r = 0; *s = 0;
  1170.         if (strcmp (r, s)) {
  1171.         *r = PATH_SEP; *s = PATH_SEP;
  1172.         break;
  1173.         } else {
  1174.         *r = PATH_SEP; *s = PATH_SEP;
  1175.         }
  1176.         p = r + 1;
  1177.         q = s + 1;
  1178.     }
  1179.     p--;
  1180.     for (i = 0; (p = strchr (p + 1, PATH_SEP)) != NULL; i++);
  1181.     currlen = i * 3 + strlen (q) + 1;
  1182.     if (j) {
  1183.         if (currlen < prevlen)
  1184.             free (buf);
  1185.         else {
  1186.         free (first);
  1187.         free (second);
  1188.         return buf;
  1189.         }
  1190.     }
  1191.     p = buf = xmalloc (currlen, "diff 2 paths");
  1192.     prevlen = currlen;
  1193.     for (; i >= 0; i--, p += 3)
  1194.       strcpy (p, "../");
  1195.     strcpy (p, q);
  1196.     }
  1197.     free (first);
  1198.     free (second);
  1199.     return buf;
  1200. }
  1201.  
  1202. #ifndef HAVE_TRUNCATE
  1203. /* On SCO and Windows NT systems */
  1204. int my_ftruncate (int fd, long size)
  1205. {
  1206. #ifdef _OS_NT
  1207.     if(_chsize(fd, size))
  1208.     return -1;
  1209.     else 
  1210.     return 0;
  1211. #else
  1212.     struct flock lk;
  1213.     
  1214.     lk.l_whence = 0;
  1215.     lk.l_start = size;
  1216.     lk.l_len = 0;
  1217.     
  1218.     return fcntl (fd, F_FREESP, &lk);
  1219. #endif
  1220. }
  1221.  
  1222. int truncate (char *path, long size)
  1223. {
  1224.     int fd;
  1225.     int res;
  1226.     
  1227.     fd = open (path, O_RDWR, 0);
  1228.     if (fd < 0)
  1229.     return fd;
  1230.     res = my_ftruncate (fd, size);
  1231.     if (res < 0)
  1232.     return res;
  1233.     close (fd);
  1234.     return 0;
  1235.  
  1236. }
  1237.  
  1238. #endif
  1239.  
  1240.